﻿
#region Using Directives.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
using System.Xml;

#endregion

namespace S3.WMDSLEditor.Client.Forms
{
    public partial class FormEditAttribute : Form
    {
        #region Fields.

        private readonly XmlDocument _xmlDocument;
        private XmlDocument _returnDocument;
        private int _currentIndex;
        private XmlNodeList _xmlNodeList;
        private XmlNode _propertyNode;
        private ViewAttribute _currentViewAttribute;
        private Dictionary<string, ViewAttribute> _viewAttributeCollection = new Dictionary<string, ViewAttribute>();
        private ViewClassProperty _viewClassProperty;

        #endregion

        #region Constructor.

        public FormEditAttribute(XmlDocument xmlDocument)
        {
            InitializeComponent();

            _xmlDocument = xmlDocument;

            _returnDocument = xmlDocument;
        }

        #endregion

        #region Properties.

        public XmlDocument GetXmlDocument
        {
            get { return _returnDocument; }
        }

        #endregion

        #region Events.

        private void FormAddAttribute_Load(object sender, EventArgs e)
        {
            string xPath = string.Format("/viewClass/Attributes/viewAttribute");
            string xPath1 = string.Format("/viewClass/properties/elementProperty");

            _xmlNodeList = _xmlDocument.SelectNodes(xPath);

            _propertyNode = _xmlDocument.SelectSingleNode(xPath1);

            InitViewData();

            SetViewData();

            if(lvwAttributes.Items.Count > 0)
            {
                lvwAttributes.Items[0].Selected = true;
            }
            else
            {
                btnRemoveAttribute.Enabled = false;
                btnMoveDown.Enabled = false;
                lblAttributeInfo.Text = string.Empty;
            }

            if (_viewClassProperty != null)
            {
                ppgViewClassProperty.SelectedObject = _viewClassProperty;
            }

            if(_currentViewAttribute != null)
            {
                ppgAttributes.SelectedObject = _currentViewAttribute;
                lblAttributeInfo.Text = string.Format("{0} properties:", _currentViewAttribute.Name);
            }
            
            btnMoveUp.Enabled = false;
        }

        private void lvwAttributes_SelectedIndexChanged(object sender, EventArgs e)
        {

            if (lvwAttributes.SelectedIndices.Count > 0)
            {
                int selectedIndex = lvwAttributes.SelectedIndices[0];

                _currentIndex = selectedIndex;

                ListViewItem item = lvwAttributes.Items[selectedIndex];

                string key = item.SubItems[1].Text;

                if (_viewAttributeCollection.ContainsKey(key))
                {
                    ViewAttribute tmp;
                    if (_viewAttributeCollection.TryGetValue(key, out tmp))
                    {
                        _currentViewAttribute = tmp;

                        ppgAttributes.SelectedObject = _currentViewAttribute;

                        lblAttributeInfo.Text = string.Format("{0} properties:", _currentViewAttribute.Name);
                    }
                }
                CheckMoveUpDown();
            }
        }

        private void ppgViewClassProperty_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            switch (e.ChangedItem.Label)
            {
                case "Name":
                    _viewClassProperty.Name = e.ChangedItem.Value.ToString();
                    break;
                case "Value":
                    _viewClassProperty.Value = (bool)e.ChangedItem.Value;
                    break;
            }
        }

        private void ppgAttributes_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            switch (e.ChangedItem.Label)
            {
                case "Name":
                    if (!_viewAttributeCollection.ContainsKey(e.ChangedItem.Value.ToString()))
                    {
                        _currentViewAttribute.Name = e.ChangedItem.Value.ToString();
                    }
                    else
                    {
                        string message = string.Format("There is already an attribute with this name ({0}), select another name and try again!", e.ChangedItem.Value);
                        MessageBox.Show(this, message, @"WMDSL Editor Message", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                        _currentViewAttribute.Name = e.OldValue.ToString();
                    }
                    break;
                case "Description":
                    _currentViewAttribute.Description = e.ChangedItem.Value.ToString();
                    break;
                case "Path":
                    _currentViewAttribute.Description = e.ChangedItem.Value.ToString();
                    break;
                case "Type":
                    _currentViewAttribute.Type = e.ChangedItem.Value.ToString();
                    break;
                case "Column":
                    _currentViewAttribute.Column = e.ChangedItem.Value.ToString();
                    break;
                case "IsDerived":
                    _currentViewAttribute.IsDerived = (bool)e.ChangedItem.Value;
                    break;
                case "DisplayName":
                    _currentViewAttribute.DisplayName = e.ChangedItem.Value.ToString();
                    break;
                default:
                    break;
            }

            _currentViewAttribute.NameAndType = string.Format("{0} : {1}", _currentViewAttribute.Name, _currentViewAttribute.Type);

            ppgAttributes.SelectedObject = _currentViewAttribute;

            ReArrangeViewAttributes();

            SetViewData();

            lvwAttributes.Items[_currentIndex].Selected = true;
        }

        private void btnMoveDown_Click(object sender, EventArgs e)
        {
            if(lvwAttributes.SelectedItems.Count > 0)
            {
                MoveListViewItem(ref lvwAttributes, false);

                CheckMoveUpDown();
            }
        }

        private void btnAddAttribute_Click(object sender, EventArgs e)
        {
            ViewAttribute newAttribute = new ViewAttribute();

            newAttribute.Name = GetDefaultName();
            newAttribute.Description = string.Empty;
            newAttribute.Path = string.Empty;
            newAttribute.Type = "string";
            newAttribute.NameAndType = string.Format("{0} : {1}", newAttribute.Name, newAttribute.Type);
            newAttribute.Column = string.Empty;
            newAttribute.IsDerived = false;
            newAttribute.DisplayName = string.Empty;

            _viewAttributeCollection.Add(newAttribute.Name, newAttribute);

            SetViewData();

            _currentViewAttribute = newAttribute;

            lvwAttributes.Items[lvwAttributes.Items.Count - 1].Selected = true;
            ppgAttributes.SelectedObject = _currentViewAttribute;
            lblAttributeInfo.Text = string.Format("{0} properties:", _currentViewAttribute.Name);

            if (!btnRemoveAttribute.Enabled)
                btnRemoveAttribute.Enabled = true;

            CheckMoveUpDown();
        }

        private void btnRemoveAttribute_Click(object sender, EventArgs e)
        {
            if (lvwAttributes.SelectedItems.Count > 0)
            {
                int index = lvwAttributes.SelectedItems[0].Index;
                string key = lvwAttributes.Items[index].SubItems[1].Text;

                _viewAttributeCollection.Remove(key);

                SetViewData();

                if (lvwAttributes.Items.Count > 0)
                {
                    if (index < lvwAttributes.Items.Count)
                    {
                        lvwAttributes.Items[index].Selected = true;
                    }
                    else
                    {
                        lvwAttributes.Items[lvwAttributes.Items.Count - 1].Selected = true;
                    }

                    ppgAttributes.SelectedObject = _currentViewAttribute;
                    lblAttributeInfo.Text = string.Format("{0} properties:", _currentViewAttribute.Name);
                    CheckMoveUpDown();
                }
                else
                {
                    btnRemoveAttribute.Enabled = false;
                    ppgAttributes.SelectedObject = null;
                    lblAttributeInfo.Text = string.Empty;
                }
            }
            else
            {
                const string message = "There are no attribute selected in the list, please select an attrubute and try again!";

                MessageBox.Show(this, message, @"WMDSL Editor Message", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        #endregion

        #region Private Methods.

        private void UpdateProperties()
        {
            string xPath = string.Format("/viewClass/properties");
            XmlNode oldNode = _xmlDocument.SelectSingleNode(xPath);

            XmlNode newNode = _xmlDocument.CreateNode(XmlNodeType.Element, "properties", null);

            XmlElement elementProperty = _xmlDocument.CreateElement("elementProperty");

            XmlAttribute value = _xmlDocument.CreateAttribute("value");
            value.Value = _viewClassProperty.Value.ToString().ToLower();

            XmlAttribute name = _xmlDocument.CreateAttribute("name");
            name.Value = _viewClassProperty.Name;

            elementProperty.Attributes.Append(value);
            elementProperty.Attributes.Append(name);

            newNode.AppendChild(elementProperty);

            if (oldNode != null)
            {
                if (oldNode.ParentNode != null)
                {
                    oldNode.ParentNode.ReplaceChild(newNode, oldNode);

                    _returnDocument = _xmlDocument;
                }
            }
        }

        private void UpdateAttributes()
        {
            string xPath = string.Format("/viewClass/Attributes");
            XmlNode oldNode = _xmlDocument.SelectSingleNode(xPath);

            ReArrangeViewAttributes();

            XmlNode newNode = _xmlDocument.CreateNode(XmlNodeType.Element, "Attributes", null);

            foreach (KeyValuePair<string, ViewAttribute> keyValuePair in _viewAttributeCollection)
            {
                XmlElement viewAttribute = _xmlDocument.CreateElement("viewAttribute");

                XmlAttribute xmlAttribute1 = _xmlDocument.CreateAttribute("Name");
                xmlAttribute1.Value = keyValuePair.Key;

                XmlAttribute xmlAttribute2 = _xmlDocument.CreateAttribute("Description");
                xmlAttribute2.Value = keyValuePair.Value.Description;

                XmlAttribute xmlAttribute3 = _xmlDocument.CreateAttribute("Path");
                xmlAttribute3.Value = keyValuePair.Value.Path;

                XmlAttribute xmlAttribute4 = _xmlDocument.CreateAttribute("Type");
                xmlAttribute4.Value = keyValuePair.Value.Type;

                XmlAttribute xmlAttribute5 = _xmlDocument.CreateAttribute("NameAndType");
                xmlAttribute5.Value = string.Format("{0} : {1}", keyValuePair.Value.Name, keyValuePair.Value.Type);

                XmlAttribute xmlAttribute6 = _xmlDocument.CreateAttribute("Column");
                xmlAttribute6.Value = keyValuePair.Value.Column;

                XmlAttribute xmlAttribute7 = _xmlDocument.CreateAttribute("isDerived");
                xmlAttribute7.Value = keyValuePair.Value.IsDerived.ToString().ToLower();

                viewAttribute.Attributes.Append(xmlAttribute1);
                viewAttribute.Attributes.Append(xmlAttribute2);
                viewAttribute.Attributes.Append(xmlAttribute3);
                viewAttribute.Attributes.Append(xmlAttribute4);
                viewAttribute.Attributes.Append(xmlAttribute5);
                viewAttribute.Attributes.Append(xmlAttribute6);
                viewAttribute.Attributes.Append(xmlAttribute7);

                newNode.AppendChild(viewAttribute);
            }

            if (oldNode != null)
            {
                if (oldNode.ParentNode != null)
                {
                    oldNode.ParentNode.ReplaceChild(newNode, oldNode);

                    _returnDocument = _xmlDocument;
                }
            }
        }

        private void btnUpdate_Click(object sender, EventArgs e)
        {
            UpdateAttributes();
            UpdateProperties();
        }

        private void btnMoveUp_Click(object sender, EventArgs e)
        {
            if (lvwAttributes.SelectedItems.Count > 0)
            {
                MoveListViewItem(ref lvwAttributes, true);

                CheckMoveUpDown();
            }
        }

        private void SetViewData()
        {
            lvwAttributes.Items.Clear();
            foreach (KeyValuePair<string, ViewAttribute> row in _viewAttributeCollection)
            {
                var item = new ListViewItem {ImageIndex = 0};

                item.SubItems.Add(row.Key);
                item.SubItems.Add(row.Value.Description);
                item.SubItems.Add(row.Value.Path);
                item.SubItems.Add(row.Value.Type);
                item.SubItems.Add(row.Value.NameAndType);
                item.SubItems.Add(row.Value.Column);
                item.SubItems.Add(row.Value.IsDerived.ToString().ToLower());

                lvwAttributes.Items.Add(item);
            }
        }

        private void InitViewData()
        {
            _viewClassProperty = new ViewClassProperty();

            if(_propertyNode.Attributes != null)
            {
                _viewClassProperty.Name = _propertyNode.Attributes["name"].Value;

                _viewClassProperty.Value = _propertyNode.Attributes["value"].Value.ToLower() == "true";
            }


            foreach (XmlNode node in _xmlNodeList)
            {
                XmlAttributeCollection attributeCollection = node.Attributes;

                var obj = new ViewAttribute();

                if (attributeCollection != null)
                {
                    obj.Name = attributeCollection["Name"].Value;
                    obj.Description = attributeCollection["Description"].Value;
                    obj.Path = attributeCollection["Path"].Value;
                    obj.Type = attributeCollection["Type"].Value;
                    obj.NameAndType = string.Format("{0} : {1}", obj.Name, obj.Type);
                    obj.Column = attributeCollection["Column"].Value;

                    obj.IsDerived = attributeCollection["isDerived"].Value.ToLower() != "false";
                }

                _viewAttributeCollection.Add(obj.Name, obj);

                if (_viewAttributeCollection.Count == 1)
                    _currentViewAttribute = obj;
            }
        }

        private static void MoveListViewItem(ref ListView lv, bool moveUp)
        {
            if (lv.SelectedItems.Count > 0)
            {
                string cache;
                int selIdx = lv.SelectedItems[0].Index;

                if (moveUp)
                {
                    if (selIdx == 0)
                    {
                        return;
                    }

                    for (int i = 0; i < lv.Items[selIdx].SubItems.Count; i++)
                    {
                        cache = lv.Items[selIdx - 1].SubItems[i].Text;
                        lv.Items[selIdx - 1].SubItems[i].Text =
                        lv.Items[selIdx].SubItems[i].Text;
                        lv.Items[selIdx].SubItems[i].Text = cache;
                    }

                    lv.Items[selIdx - 1].Selected = true;
                    lv.Refresh();
                    lv.Focus();
                }
                else
                {
                    if (selIdx == lv.Items.Count - 1)
                    {
                        return;
                    }

                    for (int i = 0; i < lv.Items[selIdx].SubItems.Count; i++)
                    {
                        cache = lv.Items[selIdx + 1].SubItems[i].Text;
                        lv.Items[selIdx + 1].SubItems[i].Text =
                        lv.Items[selIdx].SubItems[i].Text;
                        lv.Items[selIdx].SubItems[i].Text = cache;
                    }
                    lv.Items[selIdx + 1].Selected = true;
                    lv.Refresh();
                    lv.Focus();
                }
            }
        }

        private void CheckMoveUpDown()
        {
            int selIdx = lvwAttributes.SelectedItems[0].Index;

            btnMoveUp.Enabled = selIdx != 0;

            btnMoveDown.Enabled = selIdx != lvwAttributes.Items.Count - 1;
        }

        private void ReArrangeViewAttributes()
        {
            var tempList = new List<ViewAttribute>();

            int counter = 0;

            foreach (ListViewItem itm in lvwAttributes.Items)
            {
                ViewAttribute attribute;

                _viewAttributeCollection.TryGetValue(itm.SubItems[1].Text, out attribute);

                tempList.Insert(counter, attribute);

                counter++;
            }

            Dictionary<string, ViewAttribute> temp = tempList.ToDictionary(viewAttribute => viewAttribute.Name);

            _viewAttributeCollection.Clear();
            _viewAttributeCollection = temp;
        }
        
        private string GetDefaultName()
        {
            string name = "Attribute1";
            int counter = 2;

            while (_viewAttributeCollection.ContainsKey(name))
            {
                name = string.Format("Attribute{0}", counter);
                counter++;
            }

            return name;
        }
        
        #endregion
    }

    [DefaultProperty("Name")]
    internal class ViewClassProperty
    {
        [CategoryAttribute("View Class Property"), DescriptionAttribute("The name of the view class element property"), DefaultValue("dynqueryclass")]
        public string Name { get; set; }

        [CategoryAttribute("View Class Property"), DescriptionAttribute("The value of the view class element property"), DefaultValue(true)]
        public bool Value { get; set; }
    }

    [DefaultProperty("Name")]
    internal class ViewAttribute
    {
        private string _type = "string";

        [CategoryAttribute("Attribute Properties"), DescriptionAttribute("An unique name to identify the attribute"), DefaultValue("Attribute1")]
        public string Name { get; set; }

        [CategoryAttribute("Attribute Properties"), DescriptionAttribute("A text that describes the attribute"), DefaultValue("")]
        public string Description { get; set; }

        [CategoryAttribute("Attribute Properties"), DescriptionAttribute("The path of the attribute"), DefaultValue("")]
        public string Path { get; set; }

        [CategoryAttribute("Attribute Properties"), DescriptionAttribute("The type of the attribute"),
        TypeConverter(typeof(EditAttributeConverter)), DefaultValue("string")]
        public string Type
        {
            get { return _type; }
            set { _type = value; }
        }

        [CategoryAttribute("Read Only Properties"), DescriptionAttribute("A readonly type and name of the attribute"), ReadOnlyAttribute(true)]
        public string NameAndType { get; set; }

        [CategoryAttribute("Attribute Properties"), DescriptionAttribute("Column of the attribute"), DefaultValue("")]
        public string Column { get; set; }

        [CategoryAttribute("Attribute Properties"), DescriptionAttribute("True if the attribute is derived"), DefaultValue(false)]
        public bool IsDerived { get; set; }

        [CategoryAttribute("Attribute Properties"), DescriptionAttribute("The display name of the attribute"), DefaultValue("")]
        public string DisplayName { get; set; }
    }

    internal class EditAttributeConverter : StringConverter
    {
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return true;
        }
        
        public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
        {
            return true;
        }

        public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            return new StandardValuesCollection(new [] { "bool", "byte", "byte[]", "char", "DateTime", "decimal", "double",
                "float", "Guid", "int", "long", "short", "string" });
        }
    }
}
